package com.douyang.service.impl;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
import cn.dev33.satoken.oauth2.config.SaOAuth2ServerConfig;
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts;
import cn.dev33.satoken.oauth2.data.generate.SaOAuth2DataGenerate;
import cn.dev33.satoken.oauth2.data.model.AccessTokenModel;
import cn.dev33.satoken.oauth2.data.model.CodeModel;
import cn.dev33.satoken.oauth2.data.model.request.RequestAuthModel;
import cn.dev33.satoken.oauth2.error.SaOAuth2ErrorCode;
import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception;
import cn.dev33.satoken.oauth2.processor.SaOAuth2ServerProcessor;
import cn.dev33.satoken.oauth2.template.SaOAuth2Template;
import cn.dev33.satoken.oauth2.template.SaOAuth2Util;
import cn.dev33.satoken.util.SaResult;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.douyang.VO.AuthClientListVO;
import com.douyang.VO.AuthClientVO;
import com.douyang.VO.UserInfoVO;
import com.douyang.base.expcetion.UserException;
import com.douyang.base.model.PageResult;
import com.douyang.base.model.Result;
import com.douyang.base.model.UserBaseInfo;
import com.douyang.base.utils.IdWorkerUtils;
import com.douyang.base.utils.UserUtil;
import com.douyang.dto.*;
import com.douyang.pojo.AuthClient;
import com.douyang.mapper.AuthClientMapper;
import com.douyang.service.AuthClientService;
import com.douyang.service.AuthService;
import com.douyang.service.UserBaseInfoService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author xiaoma
 * @since 2025-04-04
 */
@Service
public class AuthClientServiceImpl extends ServiceImpl<AuthClientMapper, AuthClient> implements AuthClientService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private UserBaseInfoService userBaseInfoService;

    @Override
    public PageResult<AuthClientListVO> getAuthClientList(GetAuthClientListDTO getAuthClientListDTO) {
        Page<AuthClient> page =lambdaQuery()
                .eq(getAuthClientListDTO.getId()!=null,AuthClient::getId,getAuthClientListDTO.getId())
                .like(StringUtils.isNotEmpty(getAuthClientListDTO.getClientName()),AuthClient::getClientName,getAuthClientListDTO.getClientName())
                .eq(getAuthClientListDTO.getStatus()!=null,AuthClient::getStatus,getAuthClientListDTO.getStatus())
                .page(getAuthClientListDTO.getPage());


        return PageResult.of(page,authClient -> {
            AuthClientListVO authClientListVO = new AuthClientListVO();
            BeanUtils.copyProperties(authClient,authClientListVO);
            authClientListVO.setScopes(JSON.parseArray(authClient.getScopesJson(),String.class));
            return authClientListVO;
        });
    }

    @Override
    public void appClient(ApplyClientDTO applyClientDTO) {
        if(StringUtils.isEmpty(applyClientDTO.getClientHostname())){throw new UserException("域名不能为空");}
        if(StringUtils.isEmpty(applyClientDTO.getClientName())){throw new UserException("应用名不能为空");}
        if(CollectionUtils.isEmpty(applyClientDTO.getScopes())){throw new UserException("请选择需要获取的授权信息");}
        if(StringUtils.isEmpty(applyClientDTO.getClientLogo()))applyClientDTO.setClientLogo(null);
        List<String>totalScopes= List.of("用户名","头像","邮箱");
        totalScopes.forEach(s->{if(!totalScopes.contains(s))throw new UserException("非法属性");});
        remove(new LambdaQueryWrapper<AuthClient>()
                .eq(AuthClient::getClientName,applyClientDTO.getClientName())
                .eq(AuthClient::getClientHostname,applyClientDTO.getClientHostname())
        );

        AuthClient authClient = new AuthClient();
        BeanUtils.copyProperties(applyClientDTO,authClient);
        authClient.setCreateTime(LocalDateTime.now());
        authClient.setScopesJson(JSON.toJSONString(applyClientDTO.getScopes()));
        authClient.setStatus(1);
        authClient.setUserId(UserUtil.getLoginUserBaseInfo().getId());
        save(authClient);
    }

    @Override
    public void audit(AuthClientStatusDTO authClientStatusDTO) {
        AuthClient authClient = getById(authClientStatusDTO.getId());
        if(authClient==null)throw new UserException("audit:id不存在");
        authClient.setStatus(authClientStatusDTO.getStatus());
        authClient.setUpdateTime(LocalDateTime.now());
        authClient.setUpdateUser(UserUtil.getLoginUserBaseInfo().getId());
        authClient.setFailReason(authClientStatusDTO.getFailReason());
        updateById(authClient);
    }

    @Override
    public PageResult<AuthClientListVO> getMyApplications(GetAuthClientListDTO getAuthClientListDTO) {
        Page<AuthClient> page=lambdaQuery().eq(AuthClient::getUserId,UserUtil.getLoginUserBaseInfo().getId())
                .eq(getAuthClientListDTO.getId()!=null,AuthClient::getId,getAuthClientListDTO.getId())
                .like(StringUtils.isNotEmpty(getAuthClientListDTO.getClientName()),AuthClient::getClientName,getAuthClientListDTO.getClientName())
                .eq(getAuthClientListDTO.getStatus()!=null,AuthClient::getStatus,getAuthClientListDTO.getStatus())
                .page(getAuthClientListDTO.getPage());
        return PageResult.of(page,authClient -> {
            AuthClientListVO authClientListVO = new AuthClientListVO();
            BeanUtils.copyProperties(authClient,authClientListVO);
            authClientListVO.setScopes(JSON.parseArray(authClient.getScopesJson(),String.class));
            return authClientListVO;
        });
    }

    @Override
    public UserBaseInfo doLogin(OauthLoginDTO oauthLoginDTO) {
        AuthService authService = applicationContext.getBean("password", AuthService.class);
        LoginDTO loginDTO=new LoginDTO();
        loginDTO.setPassword(oauthLoginDTO.getPassword());
        loginDTO.setUserName(oauthLoginDTO.getUserName());
        UserBaseInfo userBaseInfo = authService.execute(loginDTO);
        return userBaseInfo;
    }

    @Override
    public AuthClientVO getAppInfo(String hostName) {
        AuthClient authClient = getByHostName(hostName);
        AuthClientVO authClientVO = new AuthClientVO();
        BeanUtils.copyProperties(authClient,authClientVO);
        authClientVO.setScopes(JSON.parseArray(authClient.getScopesJson(),String.class));
        return authClientVO;
    }

    @Override
    public Result<String> authorize() {
        SaRequest req = SaHolder.getRequest();
        SaOAuth2ServerConfig cfg = SaOAuth2Manager.getServerConfig();
        SaOAuth2DataGenerate dataGenerate = SaOAuth2Manager.getDataGenerate();
        SaOAuth2Template oauth2Template = SaOAuth2Manager.getTemplate();
        String responseType = req.getParamNotNull(SaOAuth2Consts.Param.response_type);

        // 1、先判断是否开启了指定的授权模式
        SaOAuth2ServerProcessor.instance.checkAuthorizeResponseType(responseType, req, cfg);

        // 2、如果尚未登录, 则先去登录
        long loginId = SaOAuth2Manager.getStpLogic().getLoginId(0L);
        if(loginId == 0L) {
            return new Result<String>(401, "need login", null);
        }

        // 3、构建请求 Model
        RequestAuthModel ra = SaOAuth2Manager.getDataResolver().readRequestAuthModel(req, loginId);

        // 4、校验：重定向域名是否合法
        oauth2Template.checkRedirectUri(ra.clientId, ra.redirectUri);

        // 7、判断授权类型，重定向到不同地址
        //         如果是 授权码式，则：开始重定向授权，下放code
        if(SaOAuth2Consts.ResponseType.code.equals(ra.responseType)) {
            CodeModel codeModel = dataGenerate.generateCode(ra);
            return Result.success(codeModel.code);
        }


        // 默认返回
        throw new SaOAuth2Exception("无效 response_type: " + ra.responseType).setCode(SaOAuth2ErrorCode.CODE_30125);
    }

    @Override
    public String getTokenByCode(String code) {
        SaOAuth2DataGenerate dataGenerate = SaOAuth2Manager.getDataGenerate();
        AccessTokenModel accessTokenModel = dataGenerate.generateAccessToken(code);
        return accessTokenModel.getAccessToken();
    }

    @Override
    public UserBaseInfo getUserInfoByToken(String token) {
        AccessTokenModel accessToken = SaOAuth2Util.getAccessToken(token);
        if(accessToken==null)throw new UserException("当前界面已过期");
        String clientId = accessToken.getClientId();
        Long loginId = (Long) accessToken.getLoginId();
        AuthClient authClient = getById(clientId);
        List<String> scopes = JSON.parseArray(authClient.getScopesJson(), String.class);
        UserBaseInfo userBaseInfo = userBaseInfoService.lambdaQuery().eq(UserBaseInfo::getClientId,loginId).one();
        if(userBaseInfo==null) {throw new UserException("请重新登陆");}
        if(!scopes.contains("用户名"))userBaseInfo.setUserName(null);
        if(!scopes.contains("邮箱"))userBaseInfo.setEmail(null);
        if(!scopes.contains("头像"))userBaseInfo.setAvatar(null);
        return userBaseInfo;
    }

    private AuthClient getByHostName(String hostName) {
        AuthClient authClient = lambdaQuery()
                .eq(AuthClient::getClientHostname, hostName)
                .eq(AuthClient::getStatus, 2)
                .one();

        if(authClient==null){throw new UserException("域名未注册，请联系管理员");}
        return authClient;
    }
}
